package org.jeecg.modules.flow.service.impl;

import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricVariableInstance;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.impl.util.CollectionUtil;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskInfo;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.modules.flow.entity.FlowAudit;
import org.jeecg.modules.flow.query.StartInstanceQuery;
import org.jeecg.modules.flow.query.SubmitTaskQuery;
import org.jeecg.modules.flow.service.IFlowAuditService;
import org.jeecg.modules.flow.service.IFlowBaseService;
import org.jeecg.modules.flow.service.IFlowDefService;
import org.jeecg.modules.flow.service.IFlowProcessBusinessService;
import org.jeecg.modules.flow.vo.ProInstanceVo;
import org.jeecg.modules.flow.vo.TaskVo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;

@Service
@Slf4j
public class FlowBaseServiceImpl implements IFlowBaseService {


    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private TaskService taskService;

    @Resource
    private ISysBaseAPI sysBaseAPI;

    @Autowired
    private IFlowProcessBusinessService flowProcessBusinessService;

    @Autowired
    private IFlowAuditService flowAuditService;

    @Autowired
    private IFlowDefService flowDefService;


    @Override
    public Result<String> startInstance(StartInstanceQuery query) {

        if (StringUtils.isBlank(query.getOrgId()) && query.getCusNo() == null) {
            return Result.error("企业号和机构号不能同时为空", "");
        }
        if (StringUtils.isBlank(query.getBusinessKey())) {
            return Result.error("业务种类不能为空", "");
        }
        // 核心企业号
        query.getVariables().put("cusNo", query.getCusNo());
        // 机构号
        query.getVariables().put("orgId", query.getOrgId());

        // 拆分出业务类型
        String businessType = query.getBusinessKey().split(":")[0];
        // 获取匹配流程
        String processId = flowProcessBusinessService.matchProcess(query.getCusNo(), query.getOrgId(), businessType);
        if (StringUtils.isBlank(processId)) {
            return Result.error("匹配流程为空", "");
        }
        LoginUser user = getUser();
        ProcessInstance instance = null;
        try {
            // 设置发起人
            Authentication.setAuthenticatedUserId(user.getId() + ":" + user.getRealname());
            //TODO 需要判断流程是否被挂起 否则会抛出异常
            instance = runtimeService.createProcessInstanceBuilder()
                    .processDefinitionId(processId)
                    .name(query.getName())
                    .businessKey(query.getBusinessKey())
                    .variables(query.getVariables())
                    .start();
        } catch (Exception e) {
            return Result.error("启动流程异常", "");
        }

        // 流程实例ID
        return Result.OK(instance.getId());
    }

    @Override
    public ProInstanceVo searchInstanceById(String processId) {

        ProcessInstance pi = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processId)
                .singleResult();
        if (pi == null) {
            throw new JeecgBootException("流程实例不存在");
        }
        ProInstanceVo proInstanceVo = new ProInstanceVo();
        BeanUtils.copyProperties(pi, proInstanceVo);
        if (StringUtils.isNotBlank(pi.getStartUserId()) && pi.getStartUserId().split(":").length >= 2) {
            proInstanceVo.setUserId(pi.getStartUserId().split(":")[0]);
            proInstanceVo.setUserName(pi.getStartUserId().split(":")[1]);
        }

        return proInstanceVo;
    }

    @Override
    public boolean delInstanceById(String instanceId, String reason) {
        try {
            runtimeService.deleteProcessInstance(instanceId, reason);
        } catch (Exception e) {
            log.info("删除流程实例异常:{0}", e);
            return false;
        }
        return true;
    }

    @Override
    public List<TaskVo> findTaskByAssignee(String assignee) {
        List<Task> tasks = taskService.createTaskQuery()
                .taskAssignee(assignee)
                .list();
        return getTaskVoList(tasks);
    }


    @Override
    public Map<String, Object> getVariablesByTaskId(String taskId, List<String> variableNames, boolean isHistory) {
        Map<String, Object> variables = new HashMap<>();
        if (isHistory) {
            List<HistoricVariableInstance> list = historyService.createHistoricVariableInstanceQuery()
                    .taskId(taskId)
                    .list();
            for (HistoricVariableInstance variableInstance : list) {
                variables.put(variableInstance.getVariableName(), variableInstance.getValue());
            }
        } else {
            if (variableNames.isEmpty()) {
                variables = taskService.getVariables(taskId);
            } else {
                variables = taskService.getVariables(taskId, variableNames);
            }
        }

        return variables;
    }


    @Override
    public Result<Boolean> submitTask(SubmitTaskQuery query) {
        if (query.getSubmitType() == 1) {
            // 同意
            return this.completeTask(query);
        } else if (query.getSubmitType() == 2) {
            // 退回
            return this.backProcess(query);
        } else if (query.getSubmitType() == 3) {
            // 终结
            return this.endTask(query);
        } else if (query.getSubmitType() == 4) {
            // 撤回
            return this.revocation(query);
        }
        return Result.error("未知提交类型", false);
    }

    @Override
    public ProInstanceVo getInstanceByBusinessKey(String businessKey) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceBusinessKey(businessKey)
                .singleResult();
        ProInstanceVo proInstanceVo = new ProInstanceVo();
        // processInstance是懒加载的，
        BeanUtils.copyProperties(processInstance, proInstanceVo);
        return proInstanceVo;
    }

    @Override
    public TaskVo getActiveTaskByBusinessKey(String businessKey) {
        Task task = taskService.createTaskQuery().processInstanceBusinessKey(businessKey)
                .active().singleResult();

        return getTaskVo(task);
    }

    /**
     * 拾取任务
     * 看claim的源码不用这个方法判断也可以
     *
     * @param task   任务对象
     * @param userId 用户ID
     */
    public Result<Boolean> autoClaimTask(TaskInfo task, String userId) {
        String assignee = task.getAssignee();
        // 如果任务的代理人为空
        if (StringUtils.isBlank(assignee)) {
            try {
                // 拾取任务
                taskService.claim(task.getId(), userId);
            } catch (Exception e) {
                log.error("任务不存在或任务已经被其他人拾取");
                return Result.error("任务不存在或任务已经被其他人拾取", false);
            }
        } else if (!userId.equals(assignee)) { // 任务已经有代办人且代理人不是当前用户
            log.error("任务已被其他人拾取");
            return Result.error("任务已被其他人拾取", false);
        }
        return Result.OK();
    }

    @Override
    public List<ProInstanceVo> searchInstanceByIds(Set<String> instanceIds) {
        if (instanceIds.isEmpty()) {
            return null;
        }
        final List<ProcessInstance> list = runtimeService.createProcessInstanceQuery().processInstanceIds(instanceIds).list();
        List<ProInstanceVo> instanceVos = new ArrayList<>(list.size());
        list.forEach(processInstance -> {
            ProInstanceVo temp = new ProInstanceVo();
            BeanUtils.copyProperties(processInstance, temp);
            if (StringUtils.isNotBlank(processInstance.getStartUserId()) && processInstance.getStartUserId().split(":").length >= 2) {
                temp.setUserId(processInstance.getStartUserId().split(":")[0]);
                temp.setUserName(processInstance.getStartUserId().split(":")[1]);
            }
            instanceVos.add(temp);
        });
        return instanceVos;
    }

    @Override
    public List<FlowAudit> getComment(String instanceId, String taskId) {
        final List<FlowAudit> list = flowAuditService.lambdaQuery().eq(StringUtils.isNotBlank(instanceId), FlowAudit::getProcessInstanceId, instanceId)
                .eq(StringUtils.isNotBlank(taskId), FlowAudit::getTaskId, taskId).list();
        return list;
    }

    @Override
    public List<Map<String, Object>> likeInstanceByName(String instanceName) {
        List<Map<String, Object>> result = new ArrayList<>();
        if (StringUtils.isBlank(instanceName)) {
            return result;
        }
        List<ProcessInstance> list = runtimeService.createProcessInstanceQuery().processInstanceNameLike(instanceName)
                .list();

        list.forEach(processInstance -> {
            Map<String, Object> temp = new HashMap<>(2);
            temp.put("instanceId", processInstance.getProcessInstanceId());
            temp.put("processName", processInstance.getName());
            result.add(temp);
        });
        return result;
    }

    /**
     * 完成任务
     *
     * @param query
     * @return
     */
    public Result<Boolean> completeTask(SubmitTaskQuery query) {
        // 获取到任务信息
        Task task = taskService.createTaskQuery().taskId(query.getTaskId()).singleResult();
        if (task == null) {
            log.error("任务不存在:{}", query.getTaskId());
            return Result.error("任务不存在，请刷新重试", false);
        }
        // 完成并保存审核日志
        completeTaskAndSaveAudit(task, query, getUser());
        return Result.OK("完成任务成功", true);
    }

    /**
     * 结束任务
     *
     * @param query
     */
    public Result<Boolean> endTask(SubmitTaskQuery query) {
        // 获取到任务信息
        Task task = taskService.createTaskQuery().taskId(query.getTaskId()).singleResult();
        if (task == null) {
            log.error("任务不存在{}", query.getTaskId());
            return Result.error("任务不存在", false);
        }
        // 删除并保存审核日志
        deleteProcessInstanceAndSaveAudit(task, query, getUser());
        return Result.OK("终结成功", true);
    }

    /**
     * 退回操作
     *
     * @param query
     * @return
     */
    public Result<Boolean> backProcess(SubmitTaskQuery query) {
        Task task = taskService.createTaskQuery().taskId(query.getTaskId()).singleResult();
        if (task == null) {
            return Result.error("退回失败：原因：任务为空,请刷新重试", false);
        }
        // 获取流程实例的节点信息（按创建时间升序），不包含还未创建的节点
        List<HistoricTaskInstance> hisTaskList = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(task.getProcessInstanceId())
                .orderByTaskCreateTime()
                .processUnfinished()
                .asc()
                .list();
        if (CollectionUtils.isEmpty(hisTaskList)) {
            throw new JeecgBootException("退回失败：原因：流程不存在");
        }
        HistoricTaskInstance currentTask = hisTaskList.get(hisTaskList.size() - 1);
        // 如果流程活动的节点不是传进来的节点那么该节点已经完成,不允许退回。
        if (!StringUtils.equals(currentTask.getId(), task.getId())) {
            throw new JeecgBootException("退回失败：原因：当前任务已完成不允许退回");
        }
        // 如果要退回的节点是开始节点，直接删除流程，并记录意见
        if (hisTaskList.size() < 2 || query.getBackProcessType() == 1) {
            //删除流程，并记录意见
            deleteProcessInstanceAndSaveAudit(task, query, getUser());
            return Result.OK("退回成功", true);
        }
        String prevTaskDefKey = flowDefService.prevTaskDefKey(task.getProcessDefinitionId(), task.getTaskDefinitionKey());
        if (StringUtils.isBlank(prevTaskDefKey)) {
            //删除流程，并记录意见
            deleteProcessInstanceAndSaveAudit(task, query, getUser());
            return Result.OK("退回成功", true);
        }
        // 跳转到目标节点
        try {
            jumpToTarget(task, prevTaskDefKey, query);
        } catch (Exception e) {
            return Result.error("退回失败,请刷新重试", false);
        }

        return Result.OK("回退成功", true);
    }

    /**
     * 撤回操作
     *
     * @param query
     * @return
     */
    public Result<Boolean> revocation(SubmitTaskQuery query) {

        // 获取流程实例的节点信息（按创建时间升序），不包含还未创建的节点
        List<HistoricTaskInstance> hisTaskList = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(query.getInstanceId())
                .orderByTaskCreateTime()
                .processUnfinished()
                .asc()
                .list();
        if (CollectionUtils.isEmpty(hisTaskList)) {
            return Result.error("撤回失败，原因：流程已经结束!", false);
        }

        if (hisTaskList.size() < 2) {
            return Result.error("撤回失败,原因：当前环节还未办理不支持撤回", false);
        }

        HistoricTaskInstance currentTask = null;
        for (HistoricTaskInstance historicTaskInstance : hisTaskList) {
            if (historicTaskInstance.getId().equals(query.getTaskId())) {
                currentTask = historicTaskInstance;
            }
        }

        if (currentTask == null || currentTask.getEndTime() == null) {
            return Result.error("撤回失败，原因：任务未办理或任务不存在", false);
        }
        HistoricTaskInstance activityTask = hisTaskList.get(hisTaskList.size() - 1);

        String prevTaskDefKey = flowDefService.prevTaskDefKey(activityTask.getProcessDefinitionId(), activityTask.getTaskDefinitionKey());
        if (StringUtils.isBlank(prevTaskDefKey)) {
            return Result.error("撤回失败，原因：未找到上一环节", false);
        }
        if (!prevTaskDefKey.equals(currentTask.getTaskDefinitionKey()) || activityTask.getEndTime() != null) {
            return Result.error("撤回失败，原因：下一环节已办理", false);
        }

        // 跳转到目标节点
        try {
            jumpToTarget(activityTask, currentTask.getTaskDefinitionKey(), query);
        } catch (Exception e) {
            return Result.error("撤回失败，请刷新重试！", false);
        }
        return Result.OK("撤回成功", true);
    }


    /**
     * 跳转到目标节点
     *
     * @param sourceTask
     * @param targetTaskKey
     */
    public void jumpToTarget(TaskInfo sourceTask, String targetTaskKey, SubmitTaskQuery query) {
        // 获取到流程模型
        BpmnModel bpmnModel = flowDefService.getBpmnModel(sourceTask.getProcessDefinitionId());
        //  当前节点
        FlowNode currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(sourceTask.getTaskDefinitionKey());
        //  目标节点
        FlowNode targetFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(targetTaskKey);

        //  临时保存当前活动节点的原始方向
        List<SequenceFlow> originalSequenceFlowList = new ArrayList<>(currentFlowNode.getOutgoingFlows());
        //  清理活动方向
        currentFlowNode.getOutgoingFlows().clear();
        //  建立新方向
        SequenceFlow newSequenceFlow = new SequenceFlow();
        newSequenceFlow.setId("tempSequenceFlowId");
        newSequenceFlow.setSourceFlowElement(currentFlowNode);
        newSequenceFlow.setTargetFlowElement(targetFlowNode);
        List<SequenceFlow> newSequenceFlowList = new ArrayList<>();
        newSequenceFlowList.add(newSequenceFlow);
        //  当前节点指向新的方向
        currentFlowNode.setOutgoingFlows(newSequenceFlowList);

        // 完成任务并保存审核意见
        try {
            completeTaskAndSaveAudit(sourceTask, query, getUser());
        } finally {
            //  恢复当前节点的原始方向
            currentFlowNode.setOutgoingFlows(originalSequenceFlowList);
        }
    }


    /**
     * 完成任务并保存审核意见
     *
     * @param task  任务
     * @param query 完成任务审核信息
     */
    @Transactional(rollbackFor = Exception.class)
    public void completeTaskAndSaveAudit(TaskInfo task, SubmitTaskQuery query, LoginUser user) {
        // 摄取任务
        taskService.claim(task.getId(), user.getId());
        // 完成任务
        taskService.complete(task.getId(), query.getVariables());
        // 保存审核意见
        flowAuditService.saveAudit(task, query, user);
    }

    /**
     * 删除流程并保存审核意见
     *
     * @param task  任务
     * @param query 完成任务审核信息
     */
    @Transactional(rollbackFor = Exception.class)
    public void deleteProcessInstanceAndSaveAudit(Task task, SubmitTaskQuery query, LoginUser user) {
        // 摄取任务
        taskService.claim(task.getId(), user.getId());
        // 删除流程实例
        runtimeService.deleteProcessInstance(task.getProcessInstanceId(), query.getComment());
        // 保存审核意见
        flowAuditService.saveAudit(task, query, user);
    }

    /**
     * 获取用户信息
     *
     * @return
     */
    public LoginUser getUser() {
        return (LoginUser) SecurityUtils.getSubject().getPrincipal();
    }

    /**
     * 加载任务信息
     * 由于返回的是懒加载的信息，所以还需要去调用一次get方法获取
     *
     * @param tasks 任务列表
     * @return
     */
    private List<TaskVo> getTaskVoList(List<? extends TaskInfo> tasks) {
        List<TaskVo> taskVos = new ArrayList<>(tasks.size());
        if (CollectionUtil.isEmpty(tasks)) {
            return taskVos;
        }
        tasks.forEach(task -> {
            taskVos.add(getTaskVo(task));
        });
        return taskVos;
    }

    /**
     * 加载任务信息
     * 由于返回的是懒加载的信息，所以还需要去调用一次get方法获取
     *
     * @param task 任务信息
     * @return
     */
    public TaskVo getTaskVo(TaskInfo task) {
        if (task == null) return null;
        return TaskVo.builder()
                .taskId(task.getId())
                .definitionKey(task.getTaskDefinitionKey())
                .taskName(task.getName())
                .instanceId(task.getProcessInstanceId())
                .claimTime(task.getClaimTime())
                .createTime(task.getCreateTime())
                .dueTime(task.getDueDate())
                .assignee(task.getAssignee())
                .build();
    }

    /**
     * 如果字符串为空则返回空，不为空则返回本身
     *
     * @param var
     * @return
     */
    public String condition(String var) {
        return StringUtils.isBlank(var) ? null : var;
    }

    /*    *//**
     * 转换历史流程实例信息
     *
     * @param instance 历史流程实例信息
     * @return
     *//*
    public HistoryProcessInstanceVo getHistoryProcessInstanceVo(HistoricProcessInstance instance) {
        if (instance == null) return null;
        return HistoryProcessInstanceVo.builder()
                .id(instance.getId())
                .processDefinitionKey(instance.getProcessDefinitionKey())
                .processDefinitionId(instance.getProcessDefinitionId())
                .processDefinitionName(instance.getProcessDefinitionName())
                .processDefinitionVersion(instance.getProcessDefinitionVersion())
                .name(instance.getName())
                .businessKey(instance.getBusinessKey())
                .deploymentId(instance.getDeploymentId())
                .deleteReason(instance.getDeleteReason())
                .durationInMillis(instance.getDurationInMillis())
                .startTime(instance.getStartTime())
                .endTime(instance.getEndTime())
                .startActivityId(instance.getStartActivityId())
                .endActivityId(instance.getEndActivityId())
                .deleteReason(instance.getDeleteReason())
                .build();
    }

    *//**
     * 转换历史任务
     *
     * @param task 历史任务信息
     * @return
     *//*
    public HistoryTaskInstanceVo getHistoryTaskVo(HistoricTaskInstance task) {
        if (task == null) return null;
        return HistoryTaskInstanceVo.builder()
                .taskId(task.getId())
                .taskDefinitionKey(task.getTaskDefinitionKey())
                .processInstanceId(task.getProcessInstanceId())
                .processDefinitionKey(task.getProcessDefinitionId())
                .name(task.getName())
                .claimTime(task.getClaimTime())
                .startTime(task.getStartTime())
                .endTime(task.getEndTime())
                .assignee(task.getAssignee())
                .deletedReason(task.getDeleteReason())
                .build();
    }

    *//**
     * 转换历史活动节点信息
     *
     * @param task 历史活动节点信息
     * @return
     *//*
    public HistoryActivityInstanceVo getHistoryActivityInstanceVo(HistoricActivityInstance task) {
        if (task == null) return null;
        return HistoryActivityInstanceVo.builder()
                .taskId(task.getTaskId())
                .activityId(task.getActivityId())
                .activityType(task.getActivityType())
                .processInstanceId(task.getProcessInstanceId())
                .processDefinitionId(task.getProcessDefinitionId())
                .activityName(task.getActivityName())
                .startTime(task.getStartTime())
                .endTime(task.getEndTime())
                .durationInMillis(task.getDurationInMillis())
                .assignee(task.getAssignee())
                .tenantId(task.getTenantId())
                .deleteReason(task.getDeleteReason())
                .build();
    }*/

}
